#include <clients/{{ name }}/responses.hpp>

#include <userver/clients/http/response.hpp>
#include <userver/formats/json/serialize.hpp>
#include <userver/http/common_headers.hpp>
#include <userver/http/content_type.hpp>
#include <userver/logging/log.hpp>
#include <userver/chaotic/openapi/parameters_read.hpp>

{% for include in spec.responses_definitions_includes() %}
  #include <{{ include }}>
{% endfor %}


namespace {{ namespace }} {
{% for op in operations %}
  {% if op.client_generate %}
    namespace {{ op.cpp_namespace() }} {
      Response ParseResponse(USERVER_NAMESPACE::clients::http::Response& http_response)
      {
        auto status_code = static_cast<int>(http_response.status_code());
        switch (status_code) {
        {% for response in op.responses %}
          case {{ response.status }}:
          {
            Response{{ response.status }} r{};
            {% if response.body %}
                {% if response.is_single_contenttype() %}
                  {# ignore content type for compatibility with poorly written servers #}
                  {% set body = response.single_body() %}
                  try {
                    auto json = USERVER_NAMESPACE::formats::json::FromString(http_response.body());
                    r.body = json.As<{{ body.parser_type('TODO', 'TODO') }}>();
                  } catch (const std::exception& e) {
                    LOG_ERROR() << "Failed to parse JSON response body: " << e;
                    throw ExceptionWithStatusCode({{ response.status }});
                  }
                {% else %}
                  USERVER_NAMESPACE::http::ContentType content_type = http_response.headers()[USERVER_NAMESPACE::http::headers::kContentType];

                  bool parsed = false;
                  {% for content_type, body in response.body.items() %}
                      if (content_type == USERVER_NAMESPACE::http::ContentType{"{{ content_type }}"}) {
                        {% if content_type == 'application/json' %}
                          try {
                            auto json = USERVER_NAMESPACE::formats::json::FromString(http_response.body());
                            r.body = json.As<{{ body.parser_type('TODO', 'TODO') }}>();
                          } catch (const std::exception& e) {
                            LOG_ERROR() << "Failed to parse JSON response body: " << e;
                            throw ExceptionWithStatusCode({{ response.status }});
                          }
                        {% else %}
                          r.body = {{ response.body_cpp_name(content_type) }}{http_response.body()};
                        {% endif %}
                        parsed = true;
                      }
                  {% endfor %}

                  if (!parsed) {
                    LOG_ERROR() << "Unsupported content-type: " << content_type;
                    throw ExceptionWithStatusCode({{ response.status }});
                  }
                {% endif %}
            {% endif %}

            {% for header in response.headers %}
            {
              static const USERVER_NAMESPACE::http::headers::PredefinedHeader kHeader("{{ header.raw_name }}");
              auto it = http_response.headers().find(kHeader);
              if (it != http_response.headers().end()) {
                namespace openapi = chaotic::openapi;
                static constexpr openapi::Name k{{ header.cpp_name }} = "{{ header.raw_name }}";
                using Header = {{ header.parser }};
                r.{{ header.cpp_name }} = openapi::ParseParameter<Header::Base>::Parse(std::string{it->second});
              }
            }
            {% endfor %}

            {% if response.is_error() %}
              throw r;
            {% else %}
              return r;
            {% endif %}
          }
        {% endfor %}
        default:
          throw ExceptionWithStatusCode(status_code);
        }
      }
    }
  {% endif %}
{% endfor %}
}
